
#include "RtBase.h"
#include "RtAcceptorTcp.h"
#include "RtInetAddr.h"
#include "RtTransportTcp.h"


//////////////////////////////////////////////////////////////////////////

CRtString RtGetSystemErrorInfo(int inErrno)
{
#define RT_ERROR_BUF_SIZE 1024
	char szErrorBuf[RT_ERROR_BUF_SIZE] = "";
	sprintf(szErrorBuf, "%d:", inErrno);
	size_t len = strlen(szErrorBuf);
#ifdef RT_WIN32
	DWORD rt = FormatMessage( 
		FORMAT_MESSAGE_FROM_SYSTEM | 
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		inErrno,
		0, 
		szErrorBuf+len,
		RT_ERROR_BUF_SIZE-1-len,
		NULL 
	);
#else
	strncpy(szErrorBuf+len, strerror(inErrno), RT_ERROR_BUF_SIZE-1-len);
#endif
	return CRtString(szErrorBuf);
}

//////////////////////////////////////////////////////////////////////////
/// class CRtAcceptorTcp
//////////////////////////////////////////////////////////////////////////
CRtAcceptorTcp::CRtAcceptorTcp()
{
}

CRtAcceptorTcp::~CRtAcceptorTcp()
{
	StopListen(RT_OK);
}

RtResult CRtAcceptorTcp::
StartListen(IRtAcceptorConnectorSink *aSink, const CRtInetAddr &aAddrListen)
{
	RtResult rv = RT_ERROR_NETWORK_SOCKET_ERROR;
	RT_ASSERTE_RETURN(m_Socket.GetHandle() == RT_INVALID_HANDLE, RT_ERROR_ALREADY_INITIALIZED);

	RT_ASSERTE(!m_pSink);
	RT_ASSERTE_RETURN(aSink, RT_ERROR_INVALID_ARG);
	m_pSink = aSink;
	
	/// win2000 will allow 2 processes listening on the same port 
	/// if the socket's option allows ReuseAddress.
#ifdef WIN32
	BOOL bReuseAddr = FALSE;
#else // !WIN32
	BOOL bReuseAddr = TRUE;
#endif // WIN32
	int nRet = m_Socket.Open(bReuseAddr);
	if (nRet == -1) {
		RT_ERROR_TRACE_THIS("CRtAcceptorTcp::StartListen, Open() failed!"
			" addr=" << aAddrListen.GetIpDisplayName() <<
			" port=" << aAddrListen.GetPort() <<
			" err=" << RtGetSystemErrorInfo(errno));
		goto fail;
	}

	nRet = ::bind(
		(RT_SOCKET)m_Socket.GetHandle(), 
		reinterpret_cast<const struct sockaddr *>(aAddrListen.GetPtr()), 
		aAddrListen.GetSize());
#ifdef RT_WIN32
	if (nRet == SOCKET_ERROR) {
		errno = ::WSAGetLastError();
		nRet = -1;
	}
#endif // ! RT_WIN32
	if (nRet == -1) {
		RT_ERROR_TRACE_THIS("CRtAcceptorTcp::StartListen, bind() failed!"
			" addr=" << aAddrListen.GetIpDisplayName() <<
			" port=" << aAddrListen.GetPort() <<
			" err=" << RtGetSystemErrorInfo(errno));
		rv = RT_ERROR_NETWORK_SOCKET_ERROR;
		goto fail;
	}

	nRet = ::listen((RT_SOCKET)m_Socket.GetHandle(), 1024/*SOMAXCONN*/);
#ifdef RT_WIN32
	if (nRet == SOCKET_ERROR) {
		errno = ::WSAGetLastError();
		nRet = -1;
	}
#endif // ! RT_WIN32
	if (nRet == -1) {
		RT_ERROR_TRACE_THIS("CRtAcceptorTcp::StartListen, listen() failed! err=" << RtGetSystemErrorInfo(errno));
		rv = RT_ERROR_NETWORK_SOCKET_ERROR;
		goto fail;
	}

	rv = m_pReactor->RegisterHandler(this, IRtEventHandler::ACCEPT_MASK);
	if (RT_FAILED(rv))
		goto fail;

	RT_INFO_TRACE_THIS("CRtAcceptorTcp::StartListen,"
		" addr=" << aAddrListen.GetIpDisplayName() <<
		" port=" << aAddrListen.GetPort() << 
		" aSink=" << aSink << 
		" fd=" << m_Socket.GetHandle());
	
	return RT_OK;

fail:
	RT_ASSERTE(RT_FAILED(rv));
	StopListen(rv);
	return rv;
}

RtResult CRtAcceptorTcp::StopListen(RtResult aReason)
{
	if (m_Socket.GetHandle() != RT_INVALID_HANDLE) {
		m_pReactor->RemoveHandler(this);
		m_Socket.Close(aReason);
	}
	m_pSink = NULL;
	return RT_OK;
}

RT_HANDLE CRtAcceptorTcp::GetHandle() const 
{
	return m_Socket.GetHandle();
}

int CRtAcceptorTcp::OnInput(RT_HANDLE aFd)
{
	RT_ASSERTE(aFd == GetHandle());

	// create TCP transport with network reactor in the network thread.
	CRtTransportTcp *pTrans = new CRtTransportTcp(m_pReactorNetwork);
	if (!pTrans)
		return 0;

	CRtInetAddr addrPeer;
	int nAddrLen = addrPeer.GetSize();
	RT_HANDLE sockNew = (RT_HANDLE)::accept(
		(RT_SOCKET)GetHandle(), 
		reinterpret_cast<struct sockaddr *>(const_cast<struct sockaddr_in *>(addrPeer.GetPtr())), 
#ifdef RT_WIN32
		&nAddrLen
#else 
		reinterpret_cast<socklen_t*>(&nAddrLen)
#endif // RT_WIN32
	);

	if (sockNew == RT_INVALID_HANDLE) {
#ifdef RT_WIN32
		errno = ::WSAGetLastError();
#endif //RT_WIN32
		RT_ERROR_TRACE_THIS("CRtAcceptorTcp::OnInput, accept() failed! err=" << RtGetSystemErrorInfo(errno));
		goto fail;
	}

	pTrans->GetPeer().SetHandle(sockNew);
	if (pTrans->GetPeer().Enable(RT_IPC_SAP::NON_BLOCK) == -1) {
		RT_ERROR_TRACE_THIS("CRtAcceptorTcp::OnInput, Enable(NON_BLOCK) failed! err=" << RtGetSystemErrorInfo(errno));
		goto fail;
	}

	RT_INFO_TRACE_THIS("CRtAcceptorTcp::OnInput,"
		" addr=" << addrPeer.GetIpDisplayName() <<
		" port=" << addrPeer.GetPort() << 
		" fd=" << sockNew << 
		" trpt=" << pTrans);

	RT_ASSERTE(m_pSink);
	if (m_pSink)
		m_pSink->OnConnectIndication(RT_OK, pTrans, this);
	return 0;

fail:
	delete pTrans;
	return 0;
}

int CRtAcceptorTcp::OnClose(RT_HANDLE aFd, MASK aMask)
{
	RT_ERROR_TRACE_THIS("CRtAcceptorTcp::OnClose, it's impossible!"
		" aFd=" << aFd <<
		" aMask=" << aMask);
	return 0;
}
